package commDao

import (
	"encoding/json"
	"errors"
	"github.com/TarsCloud/TarsGo/tars"
	"gorm.io/gorm/logger"
	"reflect"
	"runtime/debug"
	"strconv"
	"xxgame.com/CommApp/Util/cache"
	"xxgame.com/CommApp/Util/goroutine"
	"xxgame.com/CommApp/Util/littleFunc"
	"xxgame.com/CommApp/Util/lock"
	"xxgame.com/CommApp/Util/log"
)

// 通过版本PstEntity：平滑升级相关组件
var gPstEntityImpVer = 2 // 1：没引入并发锁版本；2：引入并发锁以及有临时cache版本
// 获取 PstEntity 组件版本号
func GetPstEntityImpVer() int {
	return gPstEntityImpVer
}

// 持久化对象类
type PstEntity struct {
	*CommPstEntityBase
	uid     int64	// 所属对象ID（拥有者ID）
	id		int		// 对象自身ID（所在容器ID，摆入容器时，才赋值）
	RfVal   *reflect.Value
	KeyName string	// 通常是：ExtData; （除时间容器外，为 CycData）
	data    map[string]interface{} // ExtData/CycData Json反序列后数据存储
	isInitData	bool // 是否初始化赋值data
	// 并发锁相关饿属性
	SubTag string	// 子项标签
	CtnTag string	// 容器标签
}

// 创建持久化对象
//	备注：
// 		cond: 传入属性必须带Uid，否则会Panic
// 		（规则就是需要传Uid, GetTag接口需要Uid属性，并且不能为0）
//	不定参数：
// 		KeyName: string (特殊字段名称，如："CycData"/"ExtData")
func NewPstEntity(obj CommEntityIF, dao *CommDao, cond CommEntityIF,
	uid int64, params... interface{}) *PstEntity {
	var curGoid int64
	var CtnTag, SubTag string
	if 2 == GetPstEntityImpVer() {
		CtnTag, SubTag = GetTag(cond, obj)
		curGoid = goroutine.GetID()
		object_if := cache.MgrSub().Get(curGoid, SubTag)
		if nil != object_if { // 返回缓存中的对象（精确匹配）
			return object_if.(*PstEntity)
		}
	}
	var KeyName string
	if 0 < len(params) {
		KeyName = params[0].(string)
	}
	object := &PstEntity{
		CommPstEntityBase: &CommPstEntityBase{
			obj:   nil,
			dao:   nil,
			dirty: false,
			RfSelf:  nil,
		},
		uid:     uid,
		RfVal:  nil,
		KeyName: "",
		data:    nil,
		SubTag: SubTag,
		CtnTag: CtnTag,
	}
	object.Init(obj, dao, cond, uid, KeyName)
	rfTmpObject := reflect.ValueOf(object)
	object.RfSelf = &rfTmpObject
	if 2 == GetPstEntityImpVer() {
		cache.MgrSub().Add(curGoid, object, SubTag)
	}
	return object
}

// 初始化持久化对象
func (this *PstEntity) Init(obj CommEntityIF, dao *CommDao, cond CommEntityIF, uid int64, KeyName string) interface{} {
	this.CommPstEntityBase.Init(obj, dao, cond)
	this.uid = uid
	if "" != KeyName { // 非空则传入参数
		this.KeyName = KeyName
	} else {			// 默认字段名称
		this.KeyName = "ExtData"
	}
	this.id = 0
	return this
}

func (this *PstEntity) GetID() int {
	return this.id
}

func (this *PstEntity) SetID(id int) {
	if 0 < this.id {
		return
	}
	this.id = id
}

func (this *PstEntity) GetUID() int64 {
	return this.uid
}

func (this *PstEntity) SetUID(uid int64) {
	if 0 < this.uid {
		return
	}
	this.uid = uid
}

// 获取对象额外属性 interface{}
func (this *PstEntity) Get(key string, def interface{}) interface{} {
	if nil == this.data {
		tars.TLOG.Errorf("当前PstEntity对象%+v无ExtData属性:" +
			"key=%+v, stack=%+v", this.TableName(), key, string(debug.Stack()))
		return def
	}
	val, ok := this.data[key]
	if ok {
		return val
	}
	return def
}

// 获取对象额外属性 int
func (this *PstEntity) GetInt(key string, def int) int {
	tmp := this.Get(key, float64(def))
	val, ok := tmp.(float64)
	if ok {
		return int(val) // 初始值是 json 序列化后默认是 float64
	} else {
		return tmp.(int) // key 对应 val 被赋值后，val 类型变成 int 类型
	}
}

// 获取对象额外属性 string
func (this *PstEntity) GetString(key string, def string) string {
	return this.Get(key, def).(string)
}

// 获取对象额外属性 float64
func (this *PstEntity) GetFloat64(key string, def float64) float64 {
	return this.Get(key, def).(float64)
}

// 获取对象额外属性 map[int]int
func (this *PstEntity) GetMapInt(key string, def map[int]int) map[int]int {
	if nil == this.data {
		tars.TLOG.Errorf("当前PstEntity对象%+v无ExtData属性:" +
			"key=%+v, stack=%+v", this.TableName(), key, string(debug.Stack()))
		return def
	}
	val1, ok1 := this.data[key]
	if !ok1 {
		return def
	}
	val, ok := val1.(map[string]interface{}) // 首次访问需要转换
	if ok {
		ret := map[int]int{}
		for k, v := range val {
			iv, _ := strconv.Atoi(k)
			ret[iv] = int(v.(float64))
		}
		this.data[key] = ret
		return ret
	} else { 		// 二次或者以上无需转换（因为首次做缓存）
		return val1.(map[int]int)
	}
}

// 获取对象额外属性 []int
func (this *PstEntity) GetListInt(key string, def []int) []int {
	if nil == this.data {
		tars.TLOG.Errorf("当前PstEntity对象%+v无ExtData属性:" +
			"key=%+v, stack=%+v", this.TableName(), key, string(debug.Stack()))
		return def
	}
	val1, ok1 := this.data[key]
	if !ok1 {
		return def
	}
	val, ok := val1.([]interface{}) // 首次访问需要转换
	if ok {
		list := littleFunc.SliceTypeChange(val, []int{}).([]int)
		this.data[key] = list
		return list
	} else {
		return val1.([]int)
	}
}

// 设置对象额外属性
func (this *PstEntity) Set(key string, val interface{}) {
	if nil == this.data {
		tars.TLOG.Errorf("当前PstEntity对象%+v无ExtData属性:" +
			"key=%+v, stack=%+v", this.TableName(), key, string(debug.Stack()))
	}
	this.data[key] = val
	this.MarkDirty()
}

// 当key为空字符串，会将当前期的书全清空（这种情况时测试使用，业务逻辑注意使用）
func (this *PstEntity) Delete(key string) {
	if nil == this.data {
		tars.TLOG.Errorf("当前PstEntity对象%+v无ExtData属性:" +
			"key=%+v, stack=%+v", this.TableName(), key, string(debug.Stack()))
	}
	if "" != key {
		if _, ok := this.data[key]; ok {
			delete(this.data, key)
			this.MarkDirty()
		}
	} else {
		this.data = map[string]interface{}{}
		this.MarkDirty()
	}
}

// 对象加载
// 	不定参数：params...
// 		canAdd:bool	是否在查询为nil时，插入新数据
func (this *PstEntity) Load(params... interface{}) error {
	if 2 == GetPstEntityImpVer() {
		lock.MgrCtn().Lock(this.CtnTag)
		lock.MgrSub().Lock(this.SubTag)
	}
	err := this.CommPstEntityBase.Load(params...)
	if nil != err {
		// 只读情况下，抛出 ErrRecordNotFound 则需要将该错误继续返回
		canAdd := true
		if 0 < len(params) {
			canAdd = params[0].(bool)
		}
		if errors.Is(err, logger.ErrRecordNotFound) && !canAdd{
			return logger.ErrRecordNotFound
		} else if !errors.Is(err, logger.ErrRecordNotFound){
			return err // 这种情况是真有异常，比如：查询或者插入异常
		}
		return nil
	}
	// 精准匹配的情况是 loaded=true, isInitData=true, 从 NewPstEntity 接口已完成精准cache匹配
	// 模糊匹配的情况是：
	//		情况一：PstEntity单独加载：
	//		obj 也是由 NewPstEntity 接口new出来，但是无法从该接口里面完成精准匹配，
	//			需要根据自身的cond条件再从DB加载回来，跟cache里面进行模糊二次匹配ID查找cache，
	//			这种情况下：loaded=true, isInitData=false，满足条件进入模糊匹配逻辑
	//		情况二：PstEntityCnt批量加载：
	//		obj 是由上级容器批量查询回来后，再调用 NewPstEntity 接口，但是无法从这里完成精准匹配，
	//			需要根据自身的cond条件再从DB加载回来，跟cache里面进行模糊二次匹配ID查找cache，
	//			这种情况下：loaded=true, isInitData=true (有bug，无法进入模糊匹配逻辑)
	if this.IsLoaded() && this.IsInitData() { // 已加载完成不认为错误，因为有可能是cache对象
		return nil
	}
	if 2 == GetPstEntityImpVer() {
		//curGoid := goroutine.GetID()
		//object_if := cache.MgrSub().GetFuzzyMatch(curGoid, this, this.TableName(), this.SubTag)
		object_if := this.FuzzyMatch()
		if nil != object_if { // 如果模糊匹配出来的对象是同一个数据库记录对象，则给obj重新绑定
			// 数据源切换指向cache的PstEntity.obj，this.bindObject()后，
			// 所有的Get和Set操作都会指向this.obj的反射this.RfVal，
			// 虽然操作不同的PstEntity，但是操作的是同一个内存数据，
			// 但是拥有两个PstEntity对象指向同一份数据，独自维护是否脏的成员变量dirty
			// 所以这里还可以继续优化，todo：减少不同的PstEntity如果dirty都为true，多次同步DB
			this.obj = object_if.(*PstEntity).obj
		}
	}
	return this.bindObject()
}

// 模糊匹配
func (this *PstEntity) FuzzyMatch() interface{} {
	curGoid := goroutine.GetID()
	log.PST.Infof("二次模糊查找cache，PstEntity.FuzzyMatch()：curGoid=%+v, "+
				"tblName=%+v, subTag=%+v", curGoid, this.TableName(), this.SubTag)
	object_if := cache.MgrSub().GetFuzzyMatch(curGoid, this, this.TableName(), this.SubTag)
	if nil != object_if {
		log.PST.Infof("二次模糊查找cache成功，PstEntity.FuzzyMatch()：curGoid=%+v, "+
			"tblName=%+v, subTag=%+v", curGoid, this.TableName(), this.SubTag)
	} else {
		log.PST.Infof("二次模糊查找cache失败，PstEntity.FuzzyMatch()：curGoid=%+v, "+
			"tblName=%+v, subTag=%+v", curGoid, this.TableName(), this.SubTag)
	}
	return object_if
}

// 对象持久化
// 	todo: 这里看能否监控panic异常，如果有异常就不持久化，避免将异常数据写入DB
func (this *PstEntity) Save() error {
	if 2 == GetPstEntityImpVer() {
		defer lock.MgrCtn().UnLock(this.CtnTag)
		defer lock.MgrSub().UnLock(this.SubTag)
	}
	if !this.IsDirty() {
		return nil
	}
	// 这里作用主要用于，需最后一道锁之前，才持久化，减少DB压力
	if 2 == GetPstEntityImpVer() {
		if !lock.MgrSub().IsLastLock(this.SubTag) {
			return nil
		}
	}
	RfExtData := (*this.RfVal).Elem().FieldByName("ExtData")
	if RfExtData.IsValid() {
		jsonStr, err := json.Marshal(this.data)
		if nil != err {
			curGoid := goroutine.GetID()
			log.PST.Errorf("持久化异常：err=%+v, curGoid=%+v, data=%+v, " +
				"cond=%+v, obj=%+v", err.Error(), curGoid, this.data, this.cond, this.obj)
			return err
		}
		RfExtData.SetString(string(jsonStr))
	}
	return this.CommPstEntityBase.Save()
}

// 重新绑定输入的数据模型obj（！！！慎用！！！仅用于需要重算整个obj覆盖写数据的特殊场景）
func (this *PstEntity) RebindObject(obj CommEntityIF) error {
	this.obj = obj
	return this.bindObject()
}

// 获取 obj
func (this *PstEntity) GetObj() CommEntityIF {
	return this.obj
}

// 重新设置obj
func (this *PstEntity) SetObj(obj CommEntityIF) {
	this.obj = obj
}

// 判断是否绑定数据源
func (this *PstEntity) IsBindData() bool {
	if nil != this.RfVal {
		return true
	}
	return false
}

// 判断是否初始化好data
func (this *PstEntity) IsInitData() bool {
	return this.isInitData
}

// 标志初始化好data
func (this *PstEntity) SetInitData() {
	if this.isInitData {
		return
	}
	this.isInitData = true
}

// 内部成员方法：数据源绑定到 this.RfVal 上
func (this *PstEntity) bindObject() error {
	RfVal := reflect.ValueOf(this.obj)
	this.RfVal = &RfVal
	RfExtData := (*this.RfVal).Elem().FieldByName("ExtData")
	var err error
	if RfExtData.IsValid() { 	// 仅用于包含有属性 ExtData 的 PstEntity
		jsonStr := RfExtData.Interface().(string)
		if "" == jsonStr {
			jsonStr = "{}"
		}
		err = json.Unmarshal([]byte(jsonStr), &this.data)
		if nil != err {
			log.PST.Errorf("初始化数据异常，PstEntity.bindObject(), " +
				"curGoid=%+v, tbl=%+v, uid=%+v, data=%+v", goroutine.GetID(),
				this.TableName(), this.GetUID(), jsonStr)
		}
	}
	if ("" == this.KeyName || "ExtData" == this.KeyName) && nil == err {
		this.SetInitData()
	}
	return err
}

// 获取对象属性【通用interface类型】
func (this *PstEntity) GetAttrIF(attrName string) interface{} {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface()
}

// 设置对象属性【通用interface类型】
func (this *PstEntity) SetAttrIF(attrName string, value interface{}) {
	(*this.RfVal).Elem().FieldByName(attrName).Set(reflect.ValueOf(value))
}

// 获取对象整型属性【int类型】
func (this *PstEntity) GetAttrInt(attrName string) int {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(int)
}

// 设置对象整型属性【int类型】
func (this *PstEntity) SetAttrInt(attrName string, value int) {
	(*this.RfVal).Elem().FieldByName(attrName).SetInt(int64(value))
	this.MarkDirty()
}

// 获取对象整型属性【int16类型】
func (this *PstEntity) GetAttrInt16(attrName string) int16 {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(int16)
}

// 设置对象整型属性【int16类型】
func (this *PstEntity) SetAttrInt16(attrName string, value int16) {
	(*this.RfVal).Elem().FieldByName(attrName).SetInt(int64(value))
	this.MarkDirty()
}

// 获取对象整型属性【int32类型】
func (this *PstEntity) GetAttrInt32(attrName string) int32 {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(int32)
}

// 设置对象整型属性【int32类型】
func (this *PstEntity) SetAttrInt32(attrName string, value int32) {
	(*this.RfVal).Elem().FieldByName(attrName).SetInt(int64(value))
	this.MarkDirty()
}

// 获取对象整型属性【int64类型】
func (this *PstEntity) GetAttrInt64(attrName string) int64 {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(int64)
}

// 设置对象整型属性【int64类型】
func (this *PstEntity) SetAttrInt64(attrName string, value int64) {
	(*this.RfVal).Elem().FieldByName(attrName).SetInt(value)
	this.MarkDirty()
}

// 获取对象整型属性【uint类型】
func (this *PstEntity) GetAttrUint(attrName string) uint {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(uint)
}

// 设置对象整型属性【uint类型】
func (this *PstEntity) SetAttrUint(attrName string, value uint) {
	(*this.RfVal).Elem().FieldByName(attrName).SetUint(uint64(value))
	this.MarkDirty()
}

// 获取对象整型属性【uint16类型】
func (this *PstEntity) GetAttrUint16(attrName string) uint16 {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(uint16)
}

// 设置对象整型属性【uint16类型】
func (this *PstEntity) SetAttrUint16(attrName string, value uint16) {
	(*this.RfVal).Elem().FieldByName(attrName).SetUint(uint64(value))
	this.MarkDirty()
}

// 获取对象整型属性【uint32类型】
func (this *PstEntity) GetAttrUint32(attrName string) uint32 {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(uint32)
}

// 设置对象整型属性【uint32类型】
func (this *PstEntity) SetAttrUint32(attrName string, value uint32) {
	(*this.RfVal).Elem().FieldByName(attrName).SetUint(uint64(value))
	this.MarkDirty()
}

// 获取对象整型属性【uint64类型】
func (this *PstEntity) GetAttrUint64(attrName string) uint64 {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(uint64)
}

// 设置对象整型属性【uint64类型】
func (this *PstEntity) SetAttrUint64(attrName string, value uint64) {
	(*this.RfVal).Elem().FieldByName(attrName).SetUint(value)
	this.MarkDirty()
}

// 对象属性累加操作
func (this *PstEntity) AddAttrInt(attrName string, value int) {
	old := this.GetAttrInt(attrName)
	new := old + value
	this.SetAttrInt(attrName, new)
}

// 获取对象字符串属性
func (this *PstEntity) GetAttrString(attrName string) string {
	return (*this.RfVal).Elem().FieldByName(attrName).Interface().(string)
}

// 设置对象字符串属性
func (this *PstEntity) SetAttrString(attrName string, value string) {
	(*this.RfVal).Elem().FieldByName(attrName).SetString(value)
	this.MarkDirty()
}
